/**
 * Common utilities for multi-page architecture
 */

/**
 * DOM utilities
 */
export class DOMUtils {
    /**
     * Get element by ID
     * @param {string} id Element ID
     * @returns {HTMLElement|null} Element or null
     */
    static getElementById(id) {
        return document.getElementById(id);
    }

    /**
     * Get elements by class name
     * @param {string} className Class name
     * @returns {HTMLCollection} Elements
     */
    static getElementsByClassName(className) {
        return document.getElementsByClassName(className);
    }

    /**
     * Add event listener with error handling
     * @param {HTMLElement} element Target element
     * @param {string} event Event type
     * @param {Function} handler Event handler
     * @param {Object} options Event options
     */
    static addEventListener(element, event, handler, options = {}) {
        if (!element) {
            console.warn(`Cannot add event listener: element not found`);
            return;
        }

        const wrappedHandler = (e) => {
            try {
                handler(e);
            } catch (error) {
                console.error(`Error in ${event} event handler:`, error);
            }
        };

        element.addEventListener(event, wrappedHandler, options);
    }

    /**
     * Show element
     * @param {HTMLElement} element Element to show
     */
    static show(element) {
        if (element) {
            element.classList.remove('hidden');
        }
    }

    /**
     * Hide element
     * @param {HTMLElement} element Element to hide
     */
    static hide(element) {
        if (element) {
            element.classList.add('hidden');
        }
    }

    /**
     * Toggle element visibility
     * @param {HTMLElement} element Element to toggle
     */
    static toggle(element) {
        if (element) {
            element.classList.toggle('hidden');
        }
    }

    /**
     * Set element text content safely
     * @param {HTMLElement} element Target element
     * @param {string} text Text content
     */
    static setTextContent(element, text) {
        if (element) {
            element.textContent = text || '';
        }
    }

    /**
     * Set element HTML content safely
     * @param {HTMLElement} element Target element
     * @param {string} html HTML content
     */
    static setHTML(element, html) {
        if (element) {
            element.innerHTML = html || '';
        }
    }

    /**
     * Clear element content
     * @param {HTMLElement} element Target element
     */
    static clear(element) {
        if (element) {
            element.innerHTML = '';
        }
    }

    /**
     * Disable element
     * @param {HTMLElement} element Element to disable
     */
    static disable(element) {
        if (element) {
            element.disabled = true;
            element.setAttribute('disabled', 'disabled');
        }
    }

    /**
     * Enable element
     * @param {HTMLElement} element Element to enable
     */
    static enable(element) {
        if (element) {
            element.disabled = false;
            element.removeAttribute('disabled');
        }
    }
}

/**
 * Form utilities
 */
export class FormUtils {
    /**
     * Get form data as object
     * @param {HTMLFormElement} form Form element
     * @returns {Object} Form data
     */
    static getFormData(form) {
        if (!form) return {};

        const formData = new FormData(form);
        const data = {};
        
        for (const [key, value] of formData.entries()) {
            data[key] = value.trim();
        }
        
        return data;
    }

    /**
     * Validate form data
     * @param {Object} data Form data
     * @param {Object} rules Validation rules
     * @returns {Object} Validation result {isValid, errors}
     */
    static validateFormData(data, rules) {
        const errors = {};
        let isValid = true;

        Object.entries(rules).forEach(([field, rule]) => {
            const value = data[field];
            
            if (rule.required && (!value || value.length === 0)) {
                errors[field] = `${rule.label || field} is required`;
                isValid = false;
            } else if (value) {
                if (rule.minLength && value.length < rule.minLength) {
                    errors[field] = `${rule.label || field} must be at least ${rule.minLength} characters`;
                    isValid = false;
                }
                if (rule.maxLength && value.length > rule.maxLength) {
                    errors[field] = `${rule.label || field} must be no more than ${rule.maxLength} characters`;
                    isValid = false;
                }
                if (rule.pattern && !rule.pattern.test(value)) {
                    errors[field] = rule.patternMessage || `${rule.label || field} format is invalid`;
                    isValid = false;
                }
            }
        });

        return { isValid, errors };
    }

    /**
     * Display form errors
     * @param {Object} errors Error messages by field
     * @param {HTMLFormElement} form Form element
     */
    static displayErrors(errors, form) {
        if (!form) return;

        // Clear existing errors
        const existingErrors = form.querySelectorAll('.error-message');
        existingErrors.forEach(error => error.remove());

        // Add new errors
        Object.entries(errors).forEach(([field, message]) => {
            const input = form.querySelector(`[name="${field}"]`);
            if (input) {
                const errorDiv = document.createElement('div');
                errorDiv.className = 'error-message';
                errorDiv.style.color = 'var(--danger-color)';
                errorDiv.style.fontSize = 'var(--font-size-sm)';
                errorDiv.style.marginTop = 'var(--spacing-xs)';
                errorDiv.textContent = message;
                input.parentNode.appendChild(errorDiv);
            }
        });
    }

    /**
     * Clear form errors
     * @param {HTMLFormElement} form Form element
     */
    static clearErrors(form) {
        if (!form) return;
        
        const errors = form.querySelectorAll('.error-message');
        errors.forEach(error => error.remove());
    }

    /**
     * Reset form
     * @param {HTMLFormElement} form Form element
     */
    static resetForm(form) {
        if (!form) return;
        
        form.reset();
        this.clearErrors(form);
    }
}

/**
 * String utilities
 */
export class StringUtils {
    /**
     * Escape HTML characters
     * @param {string} text Text to escape
     * @returns {string} Escaped text
     */
    static escapeHTML(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    /**
     * Generate random string
     * @param {number} length String length
     * @param {string} chars Available characters
     * @returns {string} Random string
     */
    static generateRandomString(length = 8, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
        let result = '';
        for (let i = 0; i < length; i++) {
            result += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return result;
    }

    /**
     * Truncate text
     * @param {string} text Text to truncate
     * @param {number} maxLength Maximum length
     * @param {string} suffix Suffix to add
     * @returns {string} Truncated text
     */
    static truncate(text, maxLength = 50, suffix = '...') {
        if (!text || text.length <= maxLength) return text;
        return text.substring(0, maxLength - suffix.length) + suffix;
    }

    /**
     * Format timestamp
     * @param {number|Date} timestamp Timestamp
     * @returns {string} Formatted time
     */
    static formatTime(timestamp) {
        const date = new Date(timestamp);
        const now = new Date();
        
        const isToday = date.toDateString() === now.toDateString();
        
        if (isToday) {
            return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
        } else {
            return date.toLocaleDateString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
        }
    }
}

/**
 * Async utilities
 */
export class AsyncUtils {
    /**
     * Sleep for specified milliseconds
     * @param {number} ms Milliseconds to sleep
     * @returns {Promise} Promise that resolves after delay
     */
    static sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    /**
     * Retry async function with exponential backoff
     * @param {Function} fn Function to retry
     * @param {number} maxRetries Maximum retry attempts
     * @param {number} baseDelay Base delay in milliseconds
     * @returns {Promise} Promise that resolves with result or rejects
     */
    static async retry(fn, maxRetries = 3, baseDelay = 1000) {
        let lastError;
        
        for (let attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                return await fn();
            } catch (error) {
                lastError = error;
                
                if (attempt === maxRetries) {
                    throw lastError;
                }
                
                // Exponential backoff
                const delay = baseDelay * Math.pow(2, attempt);
                await this.sleep(delay);
            }
        }
    }

    /**
     * Timeout wrapper for promises
     * @param {Promise} promise Promise to wrap
     * @param {number} timeoutMs Timeout in milliseconds
     * @returns {Promise} Promise that rejects on timeout
     */
    static timeout(promise, timeoutMs = 10000) {
        return Promise.race([
            promise,
            new Promise((_, reject) => 
                setTimeout(() => reject(new Error('Operation timed out')), timeoutMs)
            )
        ]);
    }
}

/**
 * Debug utilities
 */
export class DebugUtils {
    static DEBUG_MODE = window.location.hostname === 'localhost' || window.location.search.includes('debug=true');

    /**
     * Debug log
     * @param {...any} args Arguments to log
     */
    static log(...args) {
        if (this.DEBUG_MODE) {
            console.log('[DEBUG]', ...args);
        }
    }

    /**
     * Debug warn
     * @param {...any} args Arguments to warn
     */
    static warn(...args) {
        if (this.DEBUG_MODE) {
            console.warn('[DEBUG]', ...args);
        }
    }

    /**
     * Debug error
     * @param {...any} args Arguments to error
     */
    static error(...args) {
        if (this.DEBUG_MODE) {
            console.error('[DEBUG]', ...args);
        }
    }

    /**
     * Performance timing
     * @param {string} label Timer label
     */
    static time(label) {
        if (this.DEBUG_MODE) {
            console.time(`[DEBUG] ${label}`);
        }
    }

    /**
     * End performance timing
     * @param {string} label Timer label
     */
    static timeEnd(label) {
        if (this.DEBUG_MODE) {
            console.timeEnd(`[DEBUG] ${label}`);
        }
    }
}